package com.bitmain.mmst.web.service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;

import com.bitmain.mmst.domain.DetectionMiner;
import com.bitmain.mmst.domain.DetectionShelf;
import com.bitmain.mmst.domain.MinerDetectDetail;
import com.bitmain.mmst.domain.MinerDetectFailure;
import com.bitmain.mmst.domain.MinerShelf;
import com.bitmain.mmst.domain.MinerType;
import com.bitmain.mmst.feature.cache.GlobalDataBuffer;
import com.bitmain.mmst.miner.monitor.executor.MinerCommandMessage;
import com.bitmain.mmst.miner.monitor.helper.DetectMessageParser;

public class ShelfDetector {
	
	private final AtomicInteger numAll;
	private final AtomicInteger numDetect;
	private final AtomicInteger numDetail;
	private final AtomicInteger numPassed;
	private final AtomicInteger numUnpassed;
	private final AtomicInteger numFailure;
	
	private List<DetectionMiner> miners;
	
	private Collection<MinerType> types;
	
	private MinerShelf shelf;
	
	public ShelfDetector(MinerShelf shelf) {
		miners = new ArrayList<DetectionMiner>();
		numAll = new AtomicInteger(0);
		numDetect = new AtomicInteger(0);
		numDetail= new AtomicInteger(0);
		numPassed= new AtomicInteger(0);
		numUnpassed = new AtomicInteger(0);
		numFailure = new AtomicInteger(0);
		this.shelf = shelf;
	}

	public DetectionShelf detect() {
		DetectionShelf dShelf = new DetectionShelf();
		
		if(shelf == null){
			return null;
		}else{
			int ipBeginning = shelf.getIpBeginning();
			int ipEnd = shelf.getIpEnd();
			int ignoreNum = 0;
			Integer[] ignores = shelf.getIgnores();
			if(ArrayUtils.isNotEmpty(ignores)){
				ignoreNum = ignores.length;
			}
			numAll.set(ipEnd - ipBeginning +1 - ignoreNum);
			String ipBlock = shelf.getIpBlock();
			
			List<MinerCommandMessage> messages = new ArrayList<MinerCommandMessage>();
			
			for(int i=ipBeginning;i<=ipEnd;i++){
				String host = ipBlock + "." + i; 
				if(ignoreNum > 0 && ArrayUtils.contains(ignores, i)){
					continue;
				}
				MinerCommandMessage message = new MinerCommandMessage();
				message.setIpBlock(ipBlock);
				message.setIpIndex(i);
				message.setHost(host);
				message.setPort(4028);
				messages.add(message);
			}
			
			ExecutorService service = Executors.newFixedThreadPool(messages.size());
			for(MinerCommandMessage message:messages){
				submitExecutor(service,message);
			}
			service.shutdown();
			boolean done = false;
			while(!done){
				if(service.isTerminated()){
					done = true;
					service.shutdownNow();
				}else{
					try {
						Thread.sleep(1000);
					} catch (Exception e) {
					}
				}
			}
			
			Collections.sort(miners, new Comparator<DetectionMiner>() {
				@Override
				public int compare(DetectionMiner m1, DetectionMiner m2) {
					if (m1 != null && m2 != null) {
						if (m1.getIndex() > m2.getIndex()) {
							return 1;
						} else if (m1.getIndex() < m2.getIndex()) {
							return -1;
						} else {
							return 0;
						}
					} else {
						return 0;
					}
				}
			});
			
			dShelf.setNumAll(numAll.get());
			dShelf.setNumDetect(numDetect.get());
			dShelf.setNumDetail(numDetail.get());
			dShelf.setNumPassed(numPassed.get());
			dShelf.setNumUnpassed(numUnpassed.get());
			dShelf.setNumFailure(numFailure.get());
			dShelf.setMiners(miners);
		}
		return dShelf;
	}

	private void submitExecutor(ExecutorService service, MinerCommandMessage message) {
		ShelfDetectExecutor executor = new ShelfDetectExecutor(message);
		CompletableFuture<MinerCommandMessage> future = CompletableFuture.supplyAsync(executor, service);
		future.thenAccept(m -> processMessage(m));
	}

	private void processMessage(MinerCommandMessage message) {
		try {
			numDetect.incrementAndGet();
			if(BooleanUtils.isTrue(message.getCommandable())){
				numDetail.incrementAndGet();
				MinerDetectDetail detail = DetectMessageParser.processMessage(message, types);
				if(detail!=null){
					DetectionMiner miner = new DetectionMiner();
					miner.setIp(detail.getMinerHost());
					miner.setIpBlock(message.getIpBlock());
					miner.setIndex(message.getIpIndex());
					//算力
					miner.setHashAvg(detail.getHashAvg());
					miner.setHashReal(detail.getHashReal());
					//温感
					miner.setTempNumber(detail.getTempNum());
					miner.setTempValue(detail.getTempMaxValue());
					//风扇
					miner.setFanNumber(detail.getFanNum());
					miner.setFanSpeed(detail.getFanMaxSpeed());
					//链条芯片
					miner.setChainNumber(detail.getChainNum());
					miner.setChipNumber(detail.getChipNum());
					//链条形状
					String chains = "";
					if (ArrayUtils.isNotEmpty(detail.getChainAcss())) {
						for (String chain : detail.getChainAcss()) {
							chains += chain + "\r\n";
						}
					}
					miner.setChains(chains);
					//矿机类型
					miner.setType(detail.getMinerType());
					miner.setFlag(1);
					
					MinerType type = GlobalDataBuffer.getMinerType(detail.getMinerType());
					if(type!=null){
						miner.setGoodHash(miner.getHashReal() >= type.getHashPass());
						miner.setGoodFanNum(miner.getFanNumber() >= type.getFanNum());
						if (type.getFanMaxSpeed() > 0) {
							miner.setGoodFanSpeed(miner.getFanSpeed() < type.getFanMaxSpeed());
						} else {
							miner.setGoodFanSpeed(true);
						}
						miner.setGoodTempNum(miner.getTempNumber() >= type.getTempNum());
						if (type.getTempMaxValue() > 0) {
							miner.setGoodTempValue(miner.getTempValue() < type.getTempMaxValue());
						} else {
							miner.setGoodTempValue(true);
						}
						miner.setGoodChipNum(miner.getChipNumber() >= type.getChipMinNum());
						miner.setGoodChainNum(miner.getChainNumber() >= type.getChainMinNum());
					}else{
						miner.setGoodHash(true);
						miner.setGoodFanNum(true);
						miner.setGoodFanSpeed(true);
						miner.setGoodTempNum(true);
						miner.setGoodTempValue(true);
						miner.setGoodChipNum(true);
						miner.setGoodChainNum(true);
					}
					
					if(miner.isGood()){
						numPassed.getAndIncrement();
					}else{
						numUnpassed.getAndIncrement();
					}
					miners.add(miner);
				}else{
					System.out.println("=================="+message.getHost());
				}
			}else{
				numFailure.incrementAndGet();
				MinerDetectFailure failure = DetectMessageParser.processFailure(message);
				if(failure!=null){
					DetectionMiner miner = new DetectionMiner();
					miner.setIp(failure.getMinerHost());
					miner.setIpBlock(message.getIpBlock());
					miner.setIndex(message.getIpIndex());
					miner.setFlag(2);
					miners.add(miner);
				}else{
					System.out.println("-----------------"+message.getHost());
				}
			}
		} catch (Exception e) {
			System.out.println("++++++++++++++++++++++++++++++++++++++++++");
			e.printStackTrace();
			System.out.println("++++++++++++++++++++++++++++++++++++++++++");
		}
	}
}
